﻿using DotNetOpenAuth.Messaging.Bindings;
using Inovout.Cloud.Applications.Security.Models;
using Inovout.Cloud.Applications.Security.Services;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Inovout.Web.Security.OAuth2
{
    internal class CryptoKeyStore : INonceStore, ICryptoKeyStore
    {
        #region INonceStore Members

        /// <summary>
        /// Stores a given nonce and timestamp.
        /// </summary>
        /// <param name="context">The context, or namespace, within which the
        /// <paramref name="nonce"/> must be unique.
        /// The context SHOULD be treated as case-sensitive.
        /// The value will never be <c>null</c> but may be the empty string.</param>
        /// <param name="nonce">A series of random characters.</param>
        /// <param name="timestampUtc">The UTC timestamp that together with the nonce string make it unique
        /// within the given <paramref name="context"/>.
        /// The timestamp may also be used by the data store to clear out old nonces.</param>
        /// <returns>
        /// True if the context+nonce+timestamp (combination) was not previously in the database.
        /// False if the nonce was stored previously with the same timestamp and context.
        /// </returns>
        /// <remarks>
        /// The nonce must be stored for no less than the maximum time window a message may
        /// be processed within before being discarded as an expired message.
        /// This maximum message age can be looked up via the
        /// <see cref="DotNetOpenAuth.Configuration.MessagingElement.MaximumMessageLifetime"/>
        /// property, accessible via the <see cref="DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration"/>
        /// property.
        /// </remarks>
        public bool StoreNonce(string context, string nonce, DateTime timestampUtc)
        {
            NonceService nonceService = ComponentRegistry.Resolve<NonceService>();
            if (nonceService.Exists(context, nonce, timestampUtc))
            {
                return true;
            }
            nonceService.Save(new Nonce { Code = nonce, Context = context, Timestamp = timestampUtc });
            return true;
        }

        #endregion

        #region ICryptoKeyStore Members

        public CryptoKey GetKey(string bucket, string handle)
        {
            List<CryptoKey> keys = new List<CryptoKey>();
            SymmetricCryptoKeyService symmetricCryptoKeyService = ComponentRegistry.Resolve<SymmetricCryptoKeyService>();

            IList<SymmetricCryptoKey> symmetricCryptoKeys = symmetricCryptoKeyService.FindByBucketAndHandle(bucket, handle);


            foreach (var m in symmetricCryptoKeys)
            {

                CryptoKey ck = new CryptoKey(m.Secret, DateTime.SpecifyKind(m.ExpiresTime, DateTimeKind.Utc));
                keys.Add(ck);
            }

            return keys.FirstOrDefault();
        }

        public IEnumerable<KeyValuePair<string, CryptoKey>> GetKeys(string bucket)
        {

            List<KeyValuePair<string, CryptoKey>> cryptokeys = new List<KeyValuePair<string, CryptoKey>>();
            SymmetricCryptoKeyService symmetricCryptoKeyService = ComponentRegistry.Resolve<SymmetricCryptoKeyService>();

            var keys = symmetricCryptoKeyService.FindByBucket(bucket);

            foreach (var key in keys)
            {
                KeyValuePair<string, CryptoKey> kvp = new KeyValuePair<string,
                    CryptoKey>(key.Handle, new CryptoKey(key.Secret, DateTime.SpecifyKind(key.ExpiresTime, DateTimeKind.Utc)));
                cryptokeys.Add(kvp);
            }

            return cryptokeys;
        }

        public void StoreKey(string bucket, string handle, CryptoKey key)
        {
            SymmetricCryptoKeyService symmetricCryptoKeyService = ComponentRegistry.Resolve<SymmetricCryptoKeyService>();

            var symmetricCryptoKey = new SymmetricCryptoKey()
            {
                Bucket = bucket,
                Handle = handle,
                Secret = key.Key,
                ExpiresTime = key.ExpiresUtc,
            };

            symmetricCryptoKeyService.Save(symmetricCryptoKey);
        }

        public void RemoveKey(string bucket, string handle)
        {

            SymmetricCryptoKeyService symmetricCryptoKeyService = ComponentRegistry.Resolve<SymmetricCryptoKeyService>();
            symmetricCryptoKeyService.Delete(bucket, handle);
        }

        #endregion
    }
}